package org.example.listener;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 方法监听器
 * 针对利用 AOP 实现额外功能，网上回答更倾向于 叫做“拦截器”
 * 通过代码也不难看出，先拦截获取入参，然后通过 joinPoint.proceed();执行方法
 * 最后再获取返回参数
 * @Author: WangShengJie
 * @Date: 2025/4/21
 */
@Aspect
@Component
@Slf4j
public class MethodListener {


    // 定义切入点：拦截指定类的方法（支持通配符）
    @Pointcut("execution(* org.example.service.*.*(..))")
    //@Pointcut("execution(* org.example.service.TestService.methodListenerTest(..))")
    public void serviceMethods() {}

    /**
     * 对应表达式 * com.example.service.*.*(..) 可分解为以下部分：
     *<p>
     * 1、返回类型‌：*
     * 匹配任意返回值类型（如 void、String 等）‌。
     *<p>
     * 2、包名‌：com.example.service
     * 精确匹配该包下的类，‌不包含子包‌（需用 .. 匹配子包）‌。
     *<p>
     * 3、‌类名‌：第一个 *
     * 匹配 com.example.service 包下的所有类（如 UserService、OrderService）。
     *<p>
     * 4、‌方法名‌：第二个 *
     * 匹配类中的所有方法（如 create()、update()）‌。
     *<p>
     * 5、‌参数列表‌：(..)
     * 匹配任意数量、任意类型的参数（包括无参方法）‌
     */

    // 方式1：通过@Around同时获取入参和返回值
    @Around("serviceMethods()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        // System.out.println("方法入参：" + Arrays.toString(args));  // 打印入参

        Object result = joinPoint.proceed();  // 执行原方法

        // System.out.println("方法返回值：" + result);  // 打印返回值
        return result;
    }



    // 方式2：分开处理入参和返回值
    //方法执行前监听
    @Before("serviceMethods()")
    public void logMethodArgs(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        // System.out.println("方法入参：" + Arrays.toString(args));
    }

    //方法执行后监听
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    //或者
    //@AfterReturning(value = "execution(* org.example.dao.CustomerRepository.save(..))",returning = "result")
    public void logMethodResult(JoinPoint joinPoint, Object result) {
        // System.err.println("方法返回值：" + result);
    }
}
